{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import h5py\n",
    "   \n",
    "def load_dataset():\n",
    "    train_dataset = h5py.File('datasets/train_catvnoncat.h5', \"r\")# 训练集位置\n",
    "    train_set_x_orig = np.array(train_dataset[\"train_set_x\"][:]) # 提取训练集特征数据\n",
    "    train_set_y_orig = np.array(train_dataset[\"train_set_y\"][:]) # 提取训练集标签\n",
    "\n",
    "    test_dataset = h5py.File('datasets/test_catvnoncat.h5', \"r\") #测试集位置\n",
    "    test_set_x_orig = np.array(test_dataset[\"test_set_x\"][:]) # 提取测试集特征数据\n",
    "    test_set_y_orig = np.array(test_dataset[\"test_set_y\"][:]) # 提取测试集标签\n",
    "\n",
    "    classes = np.array(test_dataset[\"list_classes\"][:]) # 类别信息，0表示不是猫，1表示是猫\n",
    "    train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0])) #将标签的行向量转换为列向量\n",
    "    test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0])) \n",
    "    \n",
    "    return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train_set_y=[[0 0 1 0 0 0 0 1 0 0 0 1 0 1 1 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 0 0 0 0 0 0\n",
      "  0 1 0 0 1 1 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 0 1 0 0 1 0 0\n",
      "  0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 1 1 1 0 0 1 0 0 0 0 1 0 1 0 1 1 1 1 1\n",
      "  1 0 0 0 0 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 1 1 1 1 1 0 0 0 0 1 0 1 1 1 0\n",
      "  1 1 0 0 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 0 0 1 1 1 0 0 1 1 0 1 0 1 0 0 0 0 0\n",
      "  1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0]]\n"
     ]
    }
   ],
   "source": [
    "# 查看第26张图片是什么样的\n",
    "train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()\n",
    "index = 25\n",
    "plt.imshow(train_set_x_orig[index])\n",
    "print(\"train_set_y=\"+str(train_set_y)) #查看标签类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "【使用np.squeeze：1，不使用np.squeeze： [1]】\n",
      "y=[1], it's a cat' picture\n"
     ]
    }
   ],
   "source": [
    "'''打印出当前训练图片的标签类型，np.squeeze的作用是压缩维度，train_set_y[:,index]的值为[1],压缩后变为1，然后才能进行解码，'''\n",
    "print(\"【使用np.squeeze：\" + str(np.squeeze(train_set_y[:,index])) + \"，不使用np.squeeze： \" + str(train_set_y[:,index]) + \"】\")\n",
    "print(\"y=\" + str(train_set_y[:,index]) + \", it's a \" + classes[np.squeeze(train_set_y[:,index])].decode(\"utf-8\") + \"' picture\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练集的数量: m_train = 209\n",
      "测试集的数量 : m_test = 50\n",
      "每张图片的宽/高 : num_px = 64\n",
      "每张图片的大小 : (64, 64, 3)\n",
      "训练集_图片的维数 : (209, 64, 64, 3)\n",
      "训练集_标签的维数 : (1, 209)\n",
      "测试集_图片的维数: (50, 64, 64, 3)\n",
      "测试集_标签的维数: (1, 50)\n"
     ]
    }
   ],
   "source": [
    "# train_set_x_orig是一个维度为(m_train, num_px, numpx, 3)的数组\n",
    "m_train = train_set_y.shape[1] #训练集图片数量\n",
    "m_test = test_set_y.shape[1] #测试集图片数量\n",
    "num_px = train_set_x_orig.shape[1] #训练、测试集图片大小都为64x64\n",
    "\n",
    "#现在看一看我们加载的东西的具体情况\n",
    "print (\"训练集的数量: m_train = \" + str(m_train))\n",
    "print (\"测试集的数量 : m_test = \" + str(m_test))\n",
    "print (\"每张图片的宽/高 : num_px = \" + str(num_px))\n",
    "print (\"每张图片的大小 : (\" + str(num_px) + \", \" + str(num_px) + \", 3)\")\n",
    "print (\"训练集_图片的维数 : \" + str(train_set_x_orig.shape))\n",
    "print (\"训练集_标签的维数 : \" + str(train_set_y.shape))\n",
    "print (\"测试集_图片的维数: \" + str(test_set_x_orig.shape))\n",
    "print (\"测试集_标签的维数: \" + str(test_set_y.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "'''\n",
    "这次我们需要将训练集的维度从（209， 64， 64， 3）转换为（64*64*3， 209）的格式，首先使用reshape压缩维度，然后用.T进行转置\n",
    "'''\n",
    "# 转置训练集\n",
    "train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T\n",
    "# 转置测试集\n",
    "test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练集降维最后的维度： (12288, 209)\n",
      "训练集_标签的维数 : (1, 209)\n",
      "测试集降维之后的维度: (12288, 50)\n",
      "测试集_标签的维数 : (1, 50)\n"
     ]
    }
   ],
   "source": [
    "print (\"训练集降维最后的维度： \" + str(train_set_x_flatten.shape))\n",
    "print (\"训练集_标签的维数 : \" + str(train_set_y.shape))\n",
    "print (\"测试集降维之后的维度: \" + str(test_set_x_flatten.shape))\n",
    "print (\"测试集_标签的维数 : \" + str(test_set_y.shape))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 标准化\n",
    "train_set_x = train_set_x_flatten/255\n",
    "test_set_x = train_set_x_flatten/255"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "'''\n",
    "构建神经网络的主要步骤是：\n",
    "1.定义模型结构（例如输入特征的数量）\n",
    "2.初始化模型参数\n",
    "3.循环\n",
    "    3.1 计算当前损失（正向传播）\n",
    "    3.2 计算当前梯度（反向传播）\n",
    "    3.3 更新参数（梯度下降）\n",
    "'''\n",
    "# 定义sigmoid函数\n",
    "def sigmoid(z):\n",
    "    '''\n",
    "    参数：\n",
    "        z 任何大小的标量或数组\n",
    "    返回：\n",
    "        s sigmoid（s）\n",
    "    '''\n",
    "    s = 1 / (1 + np.exp(-z))\n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================测试sigmoid函数====================\n",
      "sigmoid(0)=0.5\n",
      "sigmoid(9.2)=0.999898970806\n"
     ]
    }
   ],
   "source": [
    "# 测试sigmoid\n",
    "print(\"==================测试sigmoid函数====================\")\n",
    "print(\"sigmoid(0)=\"+str(sigmoid(0)))\n",
    "print(\"sigmoid(9.2)=\"+str(sigmoid(9.2)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [],
   "source": [
    "def initialize_with_zeros(dim):    \n",
    "    \"\"\"\n",
    "    此函数为w创建一个维度为（dim， 1）的向量，并将b初始化为0。\n",
    "    \n",
    "    参数：\n",
    "        dim 目标矢量大小\n",
    "        \n",
    "    返回：\n",
    "        w 维度为（dim， 1）的初始化向量\n",
    "        b 初始化标量（偏差）\n",
    "    \"\"\"\n",
    "    w = np.zeros(shape = (dim, 1))\n",
    "    b = 0\n",
    "    # 使用断言进行测试\n",
    "    assert(w.shape == (dim, 1))\n",
    "    assert(isinstance(b, float) or isinstance(b, int))\n",
    "    return (w, b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def propagate(w, b, X, Y):\n",
    "    \"\"\"\n",
    "    实现前向传播和后向传播的成本函数与梯度运算\n",
    "    参数：\n",
    "        w 权值参数\n",
    "        b 偏置参数，标量\n",
    "        X 矩阵（num_px*num_px*3, 训练数量）\n",
    "        Y 标签矢量\n",
    "    返回：\n",
    "        cost 逻辑回归的负对数似然成本\n",
    "        dw 相对于w的损失梯度，和w的形状相同\n",
    "        db 相对于b的损失梯度，和b的形状相同\n",
    "    \"\"\"\n",
    "    m = X.shape[1] #训练数量\n",
    "    \n",
    "    # 正向传播\n",
    "    A = sigmoid(np.dot(w.T, X) + b) #计算激活值\n",
    "    cost = (-1 / m) * np.sum(Y * np.log(A) + (1 - Y) * np.log(1 - A)) #计算损失\n",
    "    \n",
    "    # 反向传播\n",
    "    dw = (1 / m) * np.dot(X, (A - Y).T)\n",
    "    db = (1 / m) * np.sum(A - Y)\n",
    "    \n",
    "    # 断言测试\n",
    "    assert(dw.shape == w.shape)\n",
    "    assert(db.dtype == float)\n",
    "    cost = np.squeeze(cost)\n",
    "    assert(cost.shape == ())\n",
    "    \n",
    "    # 创建一个字典保存dw和db\n",
    "    grads = {\n",
    "                \"dw\": dw,\n",
    "                \"db\": db\n",
    "    }\n",
    "    return (grads, cost)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "======================测试propagate========================\n",
      "dw=[[ 0.99993216]\n",
      " [ 1.99980262]]\n",
      "db=0.499935230625\n",
      "cost=6.00006477319\n"
     ]
    }
   ],
   "source": [
    "# 测试propagate\n",
    "print(\"======================测试propagate========================\")\n",
    "# 初始化参数\n",
    "w, b, X, Y = np.array([[1], [2]]), 2, np.array([[1, 2], [3, 4]]), np.array([[1, 0]])\n",
    "grads, cost = propagate(w, b, X, Y)\n",
    "print(\"dw=\"+str(grads[\"dw\"]))\n",
    "print(\"db=\"+str(grads[\"db\"]))\n",
    "print(\"cost=\"+str(cost))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):\n",
    "    \"\"\"\n",
    "    此函数通过梯度下降法来优化w和b\n",
    "    \n",
    "    参数：\n",
    "        w 权重，大小不等的数组（num_px*num_px*3, 1）\n",
    "        b 偏差，一个标量\n",
    "        X 维度为（num_px*num_px*3, 1）\n",
    "        Y 真正的“标签”矢量（如果非猫则为0，如果是猫则为1），矩阵维度为(1,训练数据的数量)\n",
    "        num_iterations 迭代次数\n",
    "        learning_rate 学习率\n",
    "        print_cost 是否输出损失值\n",
    "        \n",
    "    返回：\n",
    "        params 包含权重w和偏置b的字典\n",
    "        grads 包含权重w和偏差b相对于成本函数的梯度的字典\n",
    "        costs 优化期间计算所有的成本列表，用于绘制学习曲线\n",
    "        \n",
    "    提示：\n",
    "    我们需要写下两个步骤并遍历它们：\n",
    "        1）计算当前参数的成本和梯度，使用propagate（）。\n",
    "        2）使用w和b的梯度下降法则更新参数。\n",
    "    \"\"\"\n",
    "    \n",
    "    costs = []\n",
    "    \n",
    "    for i in range(num_iterations):\n",
    "        \n",
    "        grads, cost = propagate(w, b, X, Y)\n",
    "        \n",
    "        dw = grads[\"dw\"]\n",
    "        db = grads[\"db\"]\n",
    "        \n",
    "        w = w - learning_rate*dw\n",
    "        b = b - learning_rate*db \n",
    "        \n",
    "        # 记录成本\n",
    "        if i % 100 == 0:\n",
    "            costs.append(cost)\n",
    "        # 打印成本数据\n",
    "        if (print_cost) and (i%100 == 0):\n",
    "            print(\"迭代的次数：%i ， 误差值为：%f\"%(i, cost))\n",
    "    params = {\n",
    "                 \"w\": w,\n",
    "                 \"b\": b\n",
    "        }\n",
    "    grads = {\n",
    "                \"dw\": dw,\n",
    "                \"db\": db\n",
    "        }\n",
    "    \n",
    "    return (params, grads, costs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================测试optimize====================\n",
      "w=[[ 0.1124579 ]\n",
      " [ 0.23106775]]\n",
      "b=1.55930492484\n",
      "dw=[[ 0.90158428]\n",
      " [ 1.76250842]]\n",
      "db=0.430462071679\n"
     ]
    }
   ],
   "source": [
    "# 测试optimize\n",
    "print(\"==================测试optimize====================\")\n",
    "w, b, X, Y = np.array([[1], [2]]), 2, np.array([[1, 2], [3, 4]]), np.array([[1, 0]])\n",
    "params, grads, costs = optimize(w, b, X, Y, num_iterations=100, learning_rate=0.009, print_cost=False)\n",
    "print(\"w=\"+str(params[\"w\"]))\n",
    "print(\"b=\"+str(params[\"b\"]))\n",
    "print(\"dw=\"+str(grads[\"dw\"]))\n",
    "print(\"db=\"+str(grads[\"db\"]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def predict(w, b, X):\n",
    "    \"\"\"\n",
    "    使用逻辑回归参数logistic(w, b)预测标签是0还是1\n",
    "    \n",
    "    参数：\n",
    "        w 权重，大小不等的数组（num_px*num_px*3, 1）\n",
    "        b 偏差，一个标量\n",
    "        X 维度为(num_px*num_px*3, 训练数据数量)的数据\n",
    "        \n",
    "    返回：\n",
    "        Y_predict 包含X中所有图片预测的一个numpy数组（向量）\n",
    "    \"\"\"\n",
    "    m = X.shape[1]\n",
    "    Y_prediction = np.zeros((1, m))\n",
    "    w = w.reshape(X.shape[0], 1)\n",
    "    \n",
    "    # 计算猫在图片中出现的概率\n",
    "    A = sigmoid(np.dot(w.T, X) + b)\n",
    "    for i in range(A.shape[1]):\n",
    "        # 将概率a[0, i]转换为实际预测p[0, i]\n",
    "        Y_prediction[0, i] = 1 if A[0, i] > 0.5 else 0\n",
    "    \n",
    "    # 使用断言\n",
    "    assert(Y_prediction.shape == (1, m))\n",
    "    \n",
    "    return Y_prediction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "====================测试predict====================\n",
      "predictions = [[ 1.  1.]]\n"
     ]
    }
   ],
   "source": [
    "# 测试predict\n",
    "print(\"====================测试predict====================\")\n",
    "w, b, X, Y = np.array([[1], [2]]), 2, np.array([[1,2], [3,4]]), np.array([[1, 0]])\n",
    "print(\"predictions = \" + str(predict(w, b, X)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# 将所有部分整合到一个model中，包括初始化参数initialize_with_zeros(),优化函数optimize,预测函数predict\n",
    "def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.05, print_cost = False):\n",
    "    \"\"\"\n",
    "    参数说明：\n",
    "        X_train 训练集 维度为（num_px*num_px*3, m_train）的numpy数组\n",
    "        Y_train 训练集标签 维度为（1, m_train）的numpy数组\n",
    "        X_test 测试集 维度为(num_px*num_px*3, m_test)的numpy数组\n",
    "        Y_test 测试集标签 维度为（1, m_test）的numpy数组\n",
    "        num_iterations 迭代次数\n",
    "        learning_rate 学习率\n",
    "        print_cost 是否输出损失值\n",
    "    \"\"\"\n",
    "    w, b = initialize_with_zeros(X_train.shape[0])\n",
    "    \n",
    "    parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost)\n",
    "    \n",
    "    # 取出参数w和b\n",
    "    w, b = parameters[\"w\"], parameters[\"b\"]\n",
    "    \n",
    "    # 预测测试\n",
    "    Y_prediction_test = predict(w, b, X_test)\n",
    "    Y_prediction_train = predict(w, b, X_train)\n",
    "    \n",
    "    # 打印测试结果\n",
    "    print(\"训练集准确性：\", format(100 - np.mean(np.abs(Y_prediction_train - Y_train))*100), \"%\") # np.abs() 计算绝对值\n",
    "    print(\"测试集准确性：\", format(100 - np.mean(np.abs(Y_prediction_test - Y_test))*100), \"%\")\n",
    "    \n",
    "    d = {\n",
    "            \"costs\" : costs,\n",
    "            \"Y_prediction_test\" : Y_prediction_test,\n",
    "            \"Y_prediciton_train\" : Y_prediction_train,\n",
    "            \"w\" : w,\n",
    "            \"b\" : b,\n",
    "            \"learning_rate\" : learning_rate,\n",
    "            \"num_iterations\" : num_iterations }\n",
    "    return d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "====================测试model====================\n",
      "迭代的次数：0 ， 误差值为：0.693147\n",
      "迭代的次数：100 ， 误差值为：0.823921\n",
      "迭代的次数：200 ， 误差值为：0.418944\n",
      "迭代的次数：300 ， 误差值为：0.617350\n",
      "迭代的次数：400 ， 误差值为：0.522116\n",
      "迭代的次数：500 ， 误差值为：0.387709\n",
      "迭代的次数：600 ， 误差值为：0.236254\n",
      "迭代的次数：700 ， 误差值为：0.154222\n",
      "迭代的次数：800 ， 误差值为：0.135328\n",
      "迭代的次数：900 ， 误差值为：0.124971\n",
      "迭代的次数：1000 ， 误差值为：0.116478\n",
      "迭代的次数：1100 ， 误差值为：0.109193\n",
      "迭代的次数：1200 ， 误差值为：0.102804\n",
      "迭代的次数：1300 ， 误差值为：0.097130\n",
      "迭代的次数：1400 ， 误差值为：0.092043\n",
      "迭代的次数：1500 ， 误差值为：0.087453\n",
      "迭代的次数：1600 ， 误差值为：0.083286\n",
      "迭代的次数：1700 ， 误差值为：0.079487\n",
      "迭代的次数：1800 ， 误差值为：0.076007\n",
      "迭代的次数：1900 ， 误差值为：0.072809\n",
      "训练集准确性： 99.52153110047847 %\n",
      "测试集准确性： 53.734384681030505 %\n"
     ]
    }
   ],
   "source": [
    "print(\"====================测试model====================\")     \n",
    "# 这里加载的是真实的数据，请参见上面的代码部分。\n",
    "d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 2000, learning_rate = 0.01, print_cost = True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYUAAAEWCAYAAACJ0YulAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3Xl8XXWd//HXJ/ueLknTLaVJaakFWUsRpYAKCG4oLiyO\nyziIZQZnceY34jijzqgz6owzowNaAQVcAQWxIghFhSKyNNRSulDovtE0bdpm3z+/P85JepveJDfL\nyU1u3s/H4z5y7jnfe8/nnqb3nXO+53yPuTsiIiIAackuQERExg6FgoiI9FAoiIhID4WCiIj0UCiI\niEgPhYKIiPRQKMiEYGaPmNlHk12HyFinUJBImdkOM7sk2XW4+xXufney6wAwsyfM7PpRWE+2mX3f\nzOrMbL+ZfXqA9teZ2U4zazSzB81sSsyyD5rZH82sycyeiLp2SR6Fgox7ZpaR7Bq6jaVagC8C84GT\ngDcD/2hml8draGanAt8FPgyUAU3At2Oa1AL/C3w1wnplDFAoSNKY2TvNbK2ZHQn/Cj09ZtnNZrbV\nzOrNbKOZvTdm2cfM7Gkz+x8zOwR8MZz3BzP7LzM7bGbbzeyKmNf0/HWeQNsKM1sVrvtxM7vVzH7U\nx2e42Mz2mNlnzGw/cKeZTTazh8ysJnz/h8xsdtj+K8BS4BYzazCzW8L5C81spZnVmtlmM/vgCGzi\njwJfcvfD7r4JuA34WB9tPwT8yt1XuXsD8C/AVWZWCODuj7v7fcC+EahLxjCFgiSFmZ0FfB/4JDCV\n4K/UFWaWHTbZSvDlWQz8K/AjM5sR8xbnAdsI/qr9Ssy8zUAJ8HXge2ZmfZTQX9ufAM+HdX2R4K/n\n/kwHphD8RX4Dwf+rO8Pnc4Bm4BYAd/8c8BRwk7sXuPtNZpYPrAzXOw24Bvi2mS2KtzIz+3YYpPEe\n68I2k4EZwIsxL30ROLWPz3BqbFt33wq0AgsG+OySYhQKkiw3AN919+fcvTM83t8KvAHA3X/m7vvc\nvcvd7wVeBZbEvH6fu/+fu3e4e3M4b6e73+7uncDdBF+KZX2sP25bM5sDnAt83t3b3P0PwIoBPksX\n8AV3b3X3Znc/5O73u3uTu9cThNZF/bz+ncAOd78z/Dx/Au4HPhCvsbv/pbtP6uPRvbdVEP48GvPS\nOqCwjxoKerUdqL2kKIWCJMtJwN/H/pULlAMzAczsIzGHlo4ApxH8Vd9td5z33N894e5N4WRBnHb9\ntZ0J1MbM62tdsWrcvaX7iZnlmdl3w07bOmAVMMnM0vt4/UnAeb22xYcI9kCGqiH8WRQzrxio76d9\nUa95/bWXFKVQkGTZDXyl11+5ee7+UzM7CbgduAmY6u6TgPVA7KGgqIb3fQ2YYmZ5MfPKB3hN71r+\nHjgFOM/di4ALw/nWR/vdwJO9tkWBu98Yb2Vmtjzsj4j32ADg7ofDz3JGzEvPADb08Rk2xLY1s3lA\nFvBKfx9cUo9CQUZDppnlxDwyCL70l5nZeRbIN7N3hB2b+QRfnDUAZvbnBHsKkXP3nUAVQed1lpmd\nD7xrkG9TSNCPcCQ8rfMLvZZXA5Uxzx8CFpjZh80sM3yca2av66PGZWFoxHvE9hn8APjnsOP7dcAn\ngLv6qPnHwLvMbGnYx/El4IHw8Bdmlm5mOUAGkBb+O2YOZqPI+KBQkNHwMMGXZPfji+5eRfAldQtw\nGNhCeGaMu28EvgE8Q/AF+nrg6VGs90PA+cAh4MvAvQT9HYn6XyAXOAg8C/ym1/JvAu8Pz0z6VvjF\nexlBB/M+gkNbXwOyGZ4vEHTY7wSeAL7u7j21hHsWSwHcfQOwjCAcDhAE81/GvNeHCf7tvkNwAkAz\nQbBLijHdZEekf2Z2L/Cyu/f+i18k5WhPQaSX8NDNPDNLs+BiryuBB5Ndl8hoGEtXX4qMFdOBBwiu\nU9gD3BieJiqS8nT4SEREeujwkYiI9Bh3h49KSkp87ty5yS5DRGRceeGFFw66e+lA7cZdKMydO5eq\nqqpklyEiMq6Y2c5E2unwkYiI9FAoiIhID4WCiIj0UCiIiEgPhYKIiPRQKIiISA+FgoiI9FAoJKix\ntYOfPr+Lri4NCyIiqUuhkKDlT27lsw+8xJpdh5NdiohIZBQKCWho7eAHzwQXA26raUxyNSIi0VEo\nJOCe53dxtLkdM9h6sGHgF4iIjFORhoKZXW5mm81si5ndHGd5sZn9ysxeNLMN4b14x5S2ji7ueGo7\n51VMYV5pAdu1pyAiKSyyUDCzdOBW4ApgEXCtmS3q1eyvgI3ufgZwMfANM8uKqqah+OXaveyva2HZ\nxfOoLMln20GFgoikrij3FJYAW9x9m7u3AfcQ3NYwlgOFZmZAAVALdERY06B0dTnfXbWNhdMLuXhB\nKRWl+ew81EinzkASkRQVZSjMAnbHPN8Tzot1C/A6YB/wEvA37t4VYU2D8tuXD7DlQAM3XjwPM2Ne\nSQHtnc6ew03JLk1EJBLJ7mh+G7AWmAmcCdxiZkW9G5nZDWZWZWZVNTU1o1KYu/OdJ7Ywe3Iu73j9\nDAAqSvMBdAhJRFJWlKGwFyiPeT47nBfrz4EHPLAF2A4s7P1G7n6buy9298WlpQPeOGhErN5xmDW7\njvCJpZVkpAebqbIkDAV1NotIiooyFFYD882sIuw8vgZY0avNLuCtAGZWBpwCbIuwpoQtf3IrU/Kz\n+ODiY7k2JT+LopwMtuu0VBFJUZHdjtPdO8zsJuBRIB34vrtvMLNl4fLlwJeAu8zsJcCAz7j7wahq\nStTm/fX87uUD/N0lC8jNSu+Zb2ZUlhZoT0FEUlak92h294eBh3vNWx4zvQ+4LMoahuK7T24lNzOd\nj5x/0gnLKkvyeWbboSRUJSISvWR3NI85e480s+LFfVy7ZA6T80+8ZKKyNJ/XjrbQ1DZmzpwVERkx\nCoVe7ngq6NK4fmlF3OUVJQUAbNcZSCKSghQKMQ43tnHP87t595kzmTkpN26bylKdgSQiqUuhEOPu\nZ3bQ3N7Jsovm9dlm7tQgFLSnICKpSKEQamrr4O4/7uCtC6exoKywz3a5WenMmpSrUBCRlKRQCN23\nejeHm9pZdnHfewndKkry2VajaxVEJPUoFID2zi5uf2o7i0+azLlzpwzYvrI0GC3VXQPjiUhqUSgA\nv173GnuPNPfblxCroiSf+pYODja0RVyZiMjomvCh4O4sf3Ir86cV8JaF0xJ6TWWpTksVkdQ04UPh\nic01vLy/nk9eNI+0NEvoNccGxlO/goiklgkfCt95ciszinN49xkzE37NzEm5ZGWkaU9BRFLOhA6F\nNbsO8/z2Wv7iggqyMhLfFOlpxtypeWzVBWwikmImdCgsf2IrxbmZXLtkzqBfW1GSryG0RSTlTNhQ\n2HKggZWbqvno+SeRnz34wWIrSwvYVdtER+eYuXuoiMiwTdhQuG3VVrIz0vjoG+cO6fUVJfnh/Zqb\nR7YwEZEkmpChsP9oC7/4014+uLicqQXZQ3qPeT33a9YhJBFJHRMyFL73h210OXxiaeWQ36N7CG2N\nlioiqWTChcLRpnZ+8twu3vH6GZRPyRvy+0zJz2JSXibbdFqqiKSQSEPBzC43s81mtsXMbo6z/P+Z\n2drwsd7MOs1s4MGHhuFHz+2ksa3/4bETVVGSz3btKYhICoksFMwsHbgVuAJYBFxrZoti27j7f7r7\nme5+JvBZ4El3r42qppb2Tu58ejsXLShl0cyiYb9fZUmB+hREJKVEuaewBNji7tvcvQ24B7iyn/bX\nAj+NsB5+/sIeDja0jcheAgSjpVbXtdLYqvs1i0hqiDIUZgG7Y57vCeedwMzygMuB+/tYfoOZVZlZ\nVU1NzZCK6ejs4rZV2zijfBJvqByZI1TdYyBpuAsRSRVjpaP5XcDTfR06cvfb3H2xuy8uLS0d0goe\nWb+fXbVN3HhRJWaJDXw3kIqe01IVCiKSGqIMhb1Aeczz2eG8eK4h4kNH51VO4TOXL+TSRdNH7D3n\nTs3HDHU2i0jKiDIUVgPzzazCzLIIvvhX9G5kZsXARcAvI6yFaYU53HjxPNITHB47ETmZ6cwszlVn\ns4ikjMEP+pMgd+8ws5uAR4F04PvuvsHMloXLl4dN3ws85u7j8s/tytJ89SmISMqILBQA3P1h4OFe\n85b3en4XcFeUdUSpsiSf+9fsxd1HrK9CRCRZxkpH87hVWVpAQ2sHNQ2tyS5FRGTYFArDVNFza04d\nQhKR8U+hMEyVpbpWQURSh0JhmGYW55Kdkca2Gp2BJCLjn0JhmNLSLLw1p/YURGT8UyiMgIqSfPUp\niEhKUCiMgIqSfHbVNtGu+zWLyDinUBgBlaUFdHQ5u2ubkl2KiMiwKBRGQIVGSxWRFKFQGAHzSnWt\ngoikBoXCCJiUl8Vk3a9ZRFKAQmGEVJYW6FoFERn3FAojRNcqiEgqUCiMkMrSfA7Ut1Lf0p7sUkRE\nhkyhMEK679e842B0p6XWKXBEJGIKhRFSWVoAENld2J7ddogz//UxfrupOpL3FxEBhcKImTMlD7Po\nTkt9aN0+uhz++cH1OkQlIpGJNBTM7HIz22xmW8zs5j7aXGxma81sg5k9GWU9UcrJTGf25NxIOpvd\nncc3HmBBWQH761r4r0c3j/g6REQgwlAws3TgVuAKYBFwrZkt6tVmEvBt4N3ufirwgajqGQ0VJQWR\nHD5av7eO/XUtfGJpJR89fy4/eHYnL+ysHfH1iIhEuaewBNji7tvcvQ24B7iyV5vrgAfcfReAux+I\nsJ7IVZbks72mEXcf0fdduXE/aQZvWTiNf3jbKcwoyuHm+1+itaNzRNcjIhJlKMwCdsc83xPOi7UA\nmGxmT5jZC2b2kXhvZGY3mFmVmVXV1NREVO7wVZbm09jWyYH6kb1f88pNBzjnpMlMLcimIDuDL7/3\nNF490MDyJ7aN6HpERJLd0ZwBnAO8A3gb8C9mtqB3I3e/zd0Xu/vi0tLS0a4xYZUl4RlII9jZvLu2\niU2v1XHporKeeW9ZWMa7z5jJrb/fwpYD9SO2LhGRKENhL1Ae83x2OC/WHuBRd29094PAKuCMCGuK\nVEX3wHgj2K/QfQrqpYumHzf/8+9aRF52Ojff/xJdXSN7uEpEJq4oQ2E1MN/MKswsC7gGWNGrzS+B\nC8wsw8zygPOATRHWFKkZRTnkZKaxfQT3FFZuqmZeaX7P8NzdSgqy+ed3LKJq52F+/PyuEVufiExs\nkYWCu3cANwGPEnzR3+fuG8xsmZktC9tsAn4DrAOeB+5w9/VR1RS1tDRj7tT8ERst9WhzO89tqz1h\nL6Hb+86exQUnl/C1R17mtaPNI7JOEZnYIu1TcPeH3X2Bu89z96+E85a7+/KYNv/p7ovc/TR3/98o\n6xkN80oLRuxahSc2H6Cjy7l00bS4y82Mr7z3NDq6uviXBzeM+FlPIjLxJLujOeV036+5rWP492te\nubGakoIsziyf3Gebk6bm8+lLF/D4pmoeWb9/2OsUkYlNoTDCKkry6exydh8e3sB4bR1dPLm5hrcs\nnEZ6mvXb9uNvquC0WUV8YcUGjjZpCAwRGTqFwgirHKFbcz63/RD1rR199ifEykhP46tXnU5tYxv/\n8ci47acXkTFAoTDCuq9V2D7M01If31hNTmYaF5xcklD702YVc/0FFdyzejfPbD00rHWLyMSlUBhh\nxXmZTM3PGtaegruzcmM1F5xcSm5WesKv+9tLFjBnSh7/9IuXaGnXEBgiMngKhQhUlAzvtNSNr9Wx\n72gLl8VcxZyI3Kx0/uOq17P9YCPf+u2rQ16/iExcCoUIVJbmD2tPYeXGaszgzQvjn4ranzedXML7\nz5nNbau2sXFf3ZBrEJGJSaEQgYqSAg42tA759pmPb6rm7DmTKS3MHtLrP/f21zEpL5ObH1hHp4bA\nEJFBUChEoPsMpB1DOIS070gz6/fWccnrBnfoKNbk/Cw+/65TWbfnKHc+vX3I7yMiE49CIQKVJUM/\nLfXYAHhDDwWAd50+g7csnMY3HnuF3bXDu2ZCRCYOhUIE5kzNI80YUmfzYxurqSjJZ15p/sCN+2Fm\nfOk9p5Fm8LkH12sIDBFJiEIhAtkZ6cyenMe2msFdq1Df0s6z2w5x6aIyzPq/ijkRsybl8v/edgqr\nXqnhl2v3Dfv9RCT1KRQiUlmaP+iB8Z58pYb2Th9Wf0JvHz5/LmfNmcS/PbSR2sa2EXtfEUlNCoWI\nVJQEoTCYwzaPb6xmSn4W55zU9wB4g5WeZnz1qtOpb2nnyw9tHLH3FZHUpFCISGVpAU1tnVTXJXa/\n5vbOLn738oGEBsAbrFOmF3LjRfN44E97Wb2jdkTfW0RSi0IhIsfOQEqsX2H19lrqWjpG9NBRrBsv\nPpnJeZl898ltkby/iKQGhUJEekZLTbBfYeWmarIy0rhwQWID4A1WblY6Hz5/Lo9vqmbrIDvARWTi\niDQUzOxyM9tsZlvM7OY4yy82s6NmtjZ8fD7KekZTWWEOuZnpCV2rcGwAvBLysjIiq+kj559EVkYa\ndzylC9pEJL7IQsHM0oFbgSuARcC1ZrYoTtOn3P3M8PFvUdUz2tLSLOxsHviv8s3V9ew53DzsC9YG\nUlKQzfvOns39a/ZwsCGxvg4RmVii3FNYAmxx923u3gbcA1wZ4frGnIrSxEZLXbkhuIr5rUMYAG+w\nrl9aQVtHFz94Zmfk6xKR8SfKUJgF7I55viec19sbzWydmT1iZqfGeyMzu8HMqsysqqamJopaIzGv\nJJ/dCdyv+fFN1ZxZPolpRTnR11RawCWvK+OHz+yguU33XBCR4yW7o3kNMMfdTwf+D3gwXiN3v83d\nF7v74tLS0lEtcDgqSvPpcthV2/feQnVdCy/uORr5oaNYN1xYyeGmdn6+Zs+orVNExocoQ2EvUB7z\nfHY4r4e717l7Qzj9MJBpZtGcfpME3bfm7K+z+fERGgBvMM6dO5kzyifxvae2aWhtETlOlKGwGphv\nZhVmlgVcA6yIbWBm0y0c5MfMloT1pMwNhueWDHxa6sqN1Zw0NY/50wpGqyzMjBuWVrLjUBMrN1aP\n2npFZOyLLBTcvQO4CXgU2ATc5+4bzGyZmS0Lm70fWG9mLwLfAq7xFBrOszg3k5KCLLb3safQ2NrB\nH7cc4pLXjcwAeIPxtlPLKJ+Sy+1P6WI2ETkmupPi6Tkk9HCvectjpm8BbomyhmSrLClgWx+npa56\npYa2zq5RPXTULSM9jb94UwVf/NVGXth5eETHWxKR8SuhPQUz+0Ai8+RE3QPjxbNyUzWT8jJZnKQv\n5A8sLqc4N5M7tLcgIqFEDx99NsF50ktlaT4HG9o42nz8/Zo7ugfAO2UaGenJOQksPzuDP3vDHH6z\nYT87Dw3+hkAiknr6/TYysyvM7P+AWWb2rZjHXUDHqFQ4zlWEnc299xZe2HmYI03tXJKEQ0exPnr+\nXDLT0vjeHzT0hYgMvKewD6gCWoAXYh4rgLdFW1pqqCwNzirqPdzFyo3VZKWnceGC5F53Ma0oh/ec\nNZP7qnZzWDfhEZnw+g0Fd3/R3e8GTnb3u8PpFQTDVxwelQrHuTlT8khPs+OuVXB3Vm6q5vx5UynI\njrSvPyHXL62kpb2LHz2roS9EJrpED2avNLMiM5tCcBXy7Wb2PxHWlTKyMtIon5x73LUKWw40sPNQ\nU1LOOopnQVkhF59Syt3P7KClXUNfiExkiYZCsbvXAVcBP3D384C3RldWaqkoyT9uT+Gx8IKxqG6o\nMxQ3LK3kYEMbD/5p78CNRSRlJRoKGWY2A/gg8FCE9aSkytICdhxspCscUuLxTdWcPruY6cXRD4CX\nqPPnTeXUmUXc/tS2njpFZOJJNBT+jeDK5K3uvtrMKoFXoysrtVSU5NPc3sn+uhYO1LewdvcRLh1D\newkQDn1xYSVbaxr5/eYDyS5HRJIkoVBw95+5++nufmP4fJu7vy/a0lJH9605tx9s5HebDuBO0k9F\njeftr5/BzOIcbluli9lEJqpEr2iebWa/MLMD4eN+M5sddXGp4thoqQ2s3FjN7Mm5LJxemOSqTpSZ\nnsbHL6jgue21vLj7SLLLEZEkSPTw0Z0Ep6LODB+/CudJAsqKssnLSmfDvjr+sOVgUgbAS9TV55ZT\nmJ2hgfJEJqhEQ6HU3e90947wcRcwfu52k2Rmwf2af7l2H60dXVw2Bg8ddSvMyeS68+bwyPr97K5t\nSnY5IjLKEg2FQ2b2Z2aWHj7+jBS678FoqCwtoLm9k6KcDM6tmJLscvr1sTfNxYA7n96R7FJEZJQl\nGgofJzgddT/wGsF9ED4WUU0pqXsMpDcvnEZmkgbAS9SM4lzefcZM7lm9i6NN7QO/QERSxmBOSf2o\nu5e6+zSCkPjX6MpKPfPCM5DGylXMA7l+aSVNbZ385PldyS5FREZRoqFweuxYR+5eC5wVTUmp6dJF\nZXz2ioVctmh6sktJyKKZRSydX8KdT2+nraMr2eWIyChJNBTSzKznTjDhGEjJH8ltHMnLyuCTF80j\nK2NsHzqK9YmllRyob2XFi/uSXYqIjJJEv6G+ATxjZl8ysy8BfwS+PtCLzOxyM9tsZlvM7OZ+2p1r\nZh1m9v4E65FRsHR+CQunF3L7qm2k0K2zRaQfiV7R/AOCwfCqw8dV7v7D/l5jZunArcAVwCLgWjNb\n1Ee7rwGPDa50iZqZ8YmllWyurmfVqweTXY6IjIKEj2W4+0Z3vyV8bEzgJUsI7ruwzd3bgHuAK+O0\n+xRwP6ABd8agd50xk7KibG7X0BciE0KUB7hnAbtjnu8J5/Uws1nAe4Hv9PdGZnaDmVWZWVVNTc2I\nFyp9y8pI48/fVMEfthxkw76jyS5HRCKW7F7P/wU+4+79nt7i7re5+2J3X1xaqgupR9u1S+aQn5XO\nHU/pPs4iqS7KUNgLlMc8nx3Oi7UYuMfMdhBcEPdtM3tPhDXJEBTnZnLNkjn86sV9vHa0OdnliEiE\nogyF1cB8M6swsyzgGoJB9Xq4e4W7z3X3ucDPgb909wcjrEmG6GNvnEunOz99fvfAjUVk3IosFNy9\nA7iJ4OY8m4D73H2DmS0zs2VRrVeiUT4lj4sWlHLv6l10dOpiNpFUFWmfgrs/7O4L3H2eu38lnLfc\n3ZfHafsxd/95lPXI8Fy3ZA7Vda387mWdKCaSqpLd0SzjyFsWTqOsKFvjIYmkMIWCJCwjPY2rF5fz\n5Cs1uteCSIpSKMigXL1kDgbcu1odziKpSKEggzJrUi4XnzKN+6p2064OZ5GUo1CQQbtuyRwO1Lfy\n203qcBZJNQoFGbSLTyllRnGOOpxFUpBCQQYtIz2Nq88t56lX1eEskmoUCjIkV59bjgE/1d6CSEpR\nKMiQzCjO5S0Ly7ivao86nEVSiEJBhuy688o52NDKyo3VyS5FREaIQkGG7KIF05g1KZefPKdDSCKp\nQqEgQ5aeZlx9bjl/2HKQHQcbk12OiIwAhYIMy9XnlpOeZtyjK5xFUoJCQYalrCiHty6cxs9f2E1b\nhzqcRcY7hYIM23XnzeFgQxuPbdyf7FJEZJgUCjJsF84vZfZkdTiLpAKFggxbWppx7ZI5/HHrIbar\nw1lkXIs0FMzscjPbbGZbzOzmOMuvNLN1ZrbWzKrM7IIo65HofOCc2WSkma5wFhnnIgsFM0sHbgWu\nABYB15rZol7Nfguc4e5nAh8H7oiqHonWtKIcLnldGT9/YQ+tHZ3JLkdEhijKPYUlwBZ33+bubcA9\nwJWxDdy9wd09fJoPODJuXXfeHGob2/jNenU4i4xXUYbCLCD25PU94bzjmNl7zexl4NcEewsnMLMb\nwsNLVTU1NZEUK8N3wcklzJmSpw5nkXEs6R3N7v4Ld18IvAf4Uh9tbnP3xe6+uLS0dHQLlISlpRnX\nLCnnue21bK1pSHY5IjIEUYbCXqA85vnscF5c7r4KqDSzkghrkoh94JzyoMNZewsi41KUobAamG9m\nFWaWBVwDrIhtYGYnm5mF02cD2cChCGuSiJUWZvO2U6fz8zV7aGlXh7PIeBNZKLh7B3AT8CiwCbjP\n3TeY2TIzWxY2ex+w3szWEpypdHVMx7OMU9edN4cjTe3qcBYZh2y8fQcvXrzYq6qqkl2G9KOry3nz\nN56grDCH+5adn+xyRAQwsxfcffFA7ZLe0Sypp/sK5+d31PJqdX2yyxGRQVAoSCTef85sMtONn+gK\nZ5FxRaEgkSgpCDqc739BHc4i44lCQSJz3XlzqGvp4NfrXkt2KSKSIIWCROb8yqlUluRrkDyRcUSh\nIJExCzqcq3Ye5hV1OIuMCwoFidT7zplNVnqaxkMSGScUChKpKflZXPH66dy/Zg/NbepwFhnrFAoS\nuWuXzKG+pYOH1u1LdikiMgCFgkTuvIopzCvN1zULIuOAQkEi193h/KddR1iz63CyyxGRfigUZFR8\nYHE5M4pz+Lt711Lf0p7sckSkDwoFGRXFuZl869qz2HO4mX/6xXrG20CMIhOFQkFGzblzp/DpSxfw\nqxf3ce/q3QO/QERGnUJBRtWNF81j6fwSvrBiA5v364I2kbFGoSCjKi3N+O8PnklhTiZ/9ZM1NLV1\nJLskEYmhUJBRV1qYzTevOZOtNQ184Zcbkl2OiMSINBTM7HIz22xmW8zs5jjLP2Rm68zsJTP7o5md\nEWU9Mna86eQSPvXmk/nZC3t4YM2eZJcjIqHIQsHM0gnuu3wFsAi41swW9Wq2HbjI3V8PfAm4Lap6\nZOz567fOZ0nFFP75wfVsrWlIdjkiQrR7CkuALe6+zd3bgHuAK2MbuPsf3b37aqZngdkR1iNjTEZ6\nGt+65iyyM9L4qx+v0c14RMaAKENhFhB73uGecF5f/gJ4JN4CM7vBzKrMrKqmpmYES5Rkm16cw39/\n8Exe3l/Pl3+9MdnliEx4Y6Kj2czeTBAKn4m33N1vc/fF7r64tLR0dIuTyL154TQ+eWElP3p2l+7S\nJpJkUYbCXqA85vnscN5xzOx04A7gSnc/FGE9Mob9w9tO4aw5k7j5/nXsOtSU7HJEJqwoQ2E1MN/M\nKswsC7gGWBHbwMzmAA8AH3b3VyKsRca4zLB/wQxu+uka2jq6kl2SyIQUWSi4ewdwE/AosAm4z903\nmNkyM1uRWPAKAAAQ6klEQVQWNvs8MBX4tpmtNbOqqOqRsa98Sh5ff/8ZrNtzlK/95uVklyMyIWVE\n+ebu/jDwcK95y2Omrweuj7IGGV8uP206H3vjXL73h+28oXIqly4qS3ZJIhPKmOhoFon12bcv5LRZ\nRfzDz15k75HmZJcjMqEoFGTMyc5I55Zrz6azy/nrn/6J9k71L4iMFoWCjElzS/L596tezws7D/M/\nK3UOgshoUSjImPXuM2Zy7ZJyvv3EVp58RRctiowGhYKMaZ9/56ksKCvg0/eu5UBdS7LLEUl5CgUZ\n03Kz0rn1urNpbOvgb+5ZS3ObxkcSiZJCQca8+WWF/NuVp/HMtkOc9aXHuP7uKu5bvZtDDa3JLk0k\n5UR6nYLISPng4nJOmpLHI+v389iG/Ty+qZo0g3NOmsxli6Zz6aIy5pbkJ7tMkXHP3D3ZNQzK4sWL\nvapKFz5PZO7Ohn11PLaxmpUbq9n0Wh0A86cVcNmpZVy6aDqnzyomLc2SXKnI2GFmL7j74gHbKRRk\nvNtd28TKMCCe31FLZ5dTVpTNJa8r49JFZZw/byrZGenJLlMkqRQKMiEdaWrjdy8f4LEN1ax6tYam\ntk4KsjO46JRSLltUxumzJzFnSh7p2ouQCUahIBNeS3snT285yMqN1Ty+qZqDDW0AZGekcfK0Ak4p\nK2TB9EIWlBWwoKyQWZNyMVNYSGpSKIjE6OxyNuw7ysuv1fNKdT2bq+t5tbqB/THXPhRkZ/SExfyy\nAk6ZXsgpZYWUFmYrLGTcSzQUdPaRTAjpacbpsydx+uxJx80/2tTOqweCkHhlfz2vVDfw+KZq7q06\ndifZ4tzMnqCoKMnnpKn5zJ2aR/mUPHIy1VchqUWhIBNacV4mi+dOYfHcKcfNP9jQyivh3kSwV1HP\nQ+te42hze08bM5helMNJU/OYOzUIi5Om5oWPfAqy9d9Lxh/91orEUVKQTUlBNm+cV3Lc/CNNbew4\n1MTOQ43sONjEztpGdh5qOq7P4th7ZB0LiinBz9mTc5k1OZdphTnq7JYxSaEgMgiT8rI4My+LM8sn\nnbCsobWDnYeCkNhxqJFd4c9nth7igTXH3548I82YMSmHmcVBSMyelMvMScH0rHBah6YkGRQKIiOk\nIDuDU2cWc+rM4hOWtbR3sru2iT1Hmtl7uJl9R5rZG04/u/UQ++ta6Op1zkdJQVZPQMwKA2N6UQ5l\nxTlML8qhtDCbzHSNVCMjK9JQMLPLgW8C6cAd7v7VXssXAncCZwOfc/f/irIekWTJyUxnflkh88sK\n4y5v7+yiuq6FvYeDsOgOjT2Hm3mlup7fbz5AS/vxNxsyCw5zTS/Koawom7KinONCY3pxDmVFORTl\nZOjsKUlYZKFgZunArcClwB5gtZmtcPeNMc1qgb8G3hNVHSLjQWZ6GrMn5zF7cl7c5e5ObWMb++ta\nOFDXyv66FvYfbaG6roX9dS3sOdzMCzsPc7ip/YTX5mam94TGtKIcSguyKS0MHiUFWT3TU/Oz1c8h\nke4pLAG2uPs2ADO7B7gS6AkFdz8AHDCzd0RYh8i4Z2ZMLchmakE2p87su11Le2dPaFSHj/1HW3qe\nv7TnCDX1rTTGGYI8zWBKfpzAOC5EspmSn8XkvCwFSIqKMhRmAbtjnu8BzhvKG5nZDcANAHPmzBl+\nZSIpKicznTlT85gzNf4eR7emtg4O1rdR09BCTX3rsUdDW/izla0HGqipb6Utzj2yzWByXhZT8oNH\nSUH3dHbP9NT8bKaG0wqR8WNcdDS7+23AbRBc0ZzkckTGvbysDOZMzRgwPNyduuYOahpaOVDfQm1j\nG7WNbRxsaKO2sZVDDW0camzjleoGDjW0cqS5nXiDJHSHyOS8zOBn/onTk/K6AySYnpSbSYY60kdd\nlKGwFyiPeT47nCci44SZUZyXSXFeJidPKxiwfUdnF4eb2qltbONQQyuHwhDpnj7c1MbhxnZ21zax\nbk8bh5vaaes4cU+kW1FORhgax8KiODeT4txMJuUd/7M499iyrAyFyVBFGQqrgflmVkEQBtcA10W4\nPhFJsoz0tJ7+B4h/plUsd6e5vZPaxjaONLVzuKmtZzr4GQTH4aY2ahpaefVAA0eb26lv6ej3ffOy\n0pmUm0nRcaERPIpygvlFuRnHpnOOPc/LSp/QZ2tFFgru3mFmNwGPEpyS+n1332Bmy8Lly81sOlAF\nFAFdZva3wCJ3r4uqLhEZO8yMvKwM8rIymD058dd1dHZR39LB0eZ2jjS3Bz+b2qhrbudIU/tx8482\ntbPjYBNHmtuoa+6gub3/+3ynpxlFORknhEVRTiaFORkU5GRQmJNJYXYGheF0MC98ZGeSk5k2boMl\n0j4Fd38YeLjXvOUx0/sJDiuJiCQsIz0tOKyUnzXo17Z1dFHf0k5dSwd1ze3UtbRT19wR/gz2Qrqn\nu9tsrT+2h9IU58ytE+pLs2NBkR2ERlFOBgXZGeRnB8FSkBX8zM/OoDCcnx8GTX520LYgO2PUO+jH\nRUeziMhIycpI6zm9dyg6OrtobO2krqWdhtYO6ls6qA+n67qnW06cv/dIC42tHTSEj/76UmLlZqb3\nhMWHzpvD9Usrh1R3ohQKIiKDkJGeRnFeGsV5mcN6n7aOruNCorG1g/rwZ2MYNo2tnTS0ttPQ2klj\nawclQwyywVAoiIgkQVZGGlkZQzsEFiWdtyUiIj0UCiIi0kOhICIiPRQKIiLSQ6EgIiI9FAoiItJD\noSAiIj0UCiIi0sM83uDnY5iZ1QA7h/jyEuDgCJYz0sZ6fTD2a1R9w6P6hmcs13eSu5cO1GjchcJw\nmFmVuy9Odh19Gev1wdivUfUNj+obnrFeXyJ0+EhERHooFEREpMdEC4Xbkl3AAMZ6fTD2a1R9w6P6\nhmes1zegCdWnICIi/ZtoewoiItIPhYKIiPRIyVAws8vNbLOZbTGzm+MsNzP7Vrh8nZmdPYq1lZvZ\n781so5ltMLO/idPmYjM7amZrw8fnR6u+cP07zOylcN1VcZYnc/udErNd1ppZnZn9ba82o779zOz7\nZnbAzNbHzJtiZivN7NXwZ9xb0w/0+xphff9pZi+H/4a/MLNJfby239+HCOv7opntjfl3fHsfr03W\n9rs3prYdZra2j9dGvv1GlLun1ANIB7YClUAW8CKwqFebtwOPAAa8AXhuFOubAZwdThcCr8Sp72Lg\noSRuwx1AST/Lk7b94vxb7ye4KCep2w+4EDgbWB8z7+vAzeH0zcDX+vgM/f6+RljfZUBGOP21ePUl\n8vsQYX1fBP4hgd+BpGy/Xsu/AXw+WdtvJB+puKewBNji7tvcvQ24B7iyV5srgR944FlgkpnNGI3i\n3P01d18TTtcDm4BZo7HuEZS07dfLW4Gt7j7UK9xHjLuvAmp7zb4SuDucvht4T5yXJvL7Gkl97v6Y\nu3eET58FZo/0ehPVx/ZLRNK2XzczM+CDwE9Her3JkIqhMAvYHfN8Dyd+6SbSJnJmNhc4C3guzuI3\nhrv1j5jZqaNaGDjwuJm9YGY3xFk+JrYfcA19/0dM5vbrVubur4XT+4GyOG3Gyrb8OMHeXzwD/T5E\n6VPhv+P3+zj8Nha231Kg2t1f7WN5MrffoKViKIwLZlYA3A/8rbvX9Vq8Bpjj7qcD/wc8OMrlXeDu\nZwJXAH9lZheO8voHZGZZwLuBn8VZnOztdwIPjiOMyfO/zexzQAfw4z6aJOv34TsEh4XOBF4jOEQz\nFl1L/3sJY/7/U6xUDIW9QHnM89nhvMG2iYyZZRIEwo/d/YHey929zt0bwumHgUwzKxmt+tx9b/jz\nAPALgl30WEndfqErgDXuXt17QbK3X4zq7sNq4c8Dcdok+3fxY8A7gQ+FwXWCBH4fIuHu1e7e6e5d\nwO19rDfZ2y8DuAq4t682ydp+Q5WKobAamG9mFeFfk9cAK3q1WQF8JDyL5g3A0Zjd/EiFxx+/B2xy\n9//uo830sB1mtoTg3+nQKNWXb2aF3dMEnZHrezVL2vaL0edfZ8ncfr2sAD4aTn8U+GWcNon8vkbC\nzC4H/hF4t7s39dEmkd+HqOqL7ad6bx/rTdr2C10CvOzue+ItTOb2G7Jk93RH8SA4O+YVgrMSPhfO\nWwYsC6cNuDVc/hKweBRru4DgMMI6YG34eHuv+m4CNhCcSfEs8MZRrK8yXO+LYQ1javuF688n+JIv\njpmX1O1HEFCvAe0Ex7X/ApgK/BZ4FXgcmBK2nQk83N/v6yjVt4XgeHz37+Hy3vX19fswSvX9MPz9\nWkfwRT9jLG2/cP5d3b93MW1HffuN5EPDXIiISI9UPHwkIiJDpFAQEZEeCgUREemhUBARkR4KBRER\n6aFQkEiY2R/Dn3PN7LoRfu9/ireuqJjZe6IaadXMGiJ634vN7KFhvsddZvb+fpbfZGYfH846ZOxR\nKEgk3P2N4eRcYFChEF4l2p/jQiFmXVH5R+Dbw32TBD5X5Ea4hu8DnxrB95MxQKEgkYj5C/irwNJw\nLPm/M7P0cBz/1eFAZ58M219sZk+Z2QpgYzjvwXAQsQ3dA4mZ2VeB3PD9fhy7rvAK6/80s/Xh+PVX\nx7z3E2b2cwvuH/DjmCuev2rBvS3Wmdl/xfkcC4BWdz8YPr/LzJabWZWZvWJm7wznJ/y54qzjK2b2\nopk9a2ZlMet5f0ybhpj36+uzXB7OW0Mw9EL3a79oZj80s6eBH/ZTq5nZLRbcm+BxYFrMe5ywnTy4\nCnpHeNW4pIik/+UiKe9mgjHxu788byAYFuNcM8sGnjazx8K2ZwOnufv28PnH3b3WzHKB1WZ2v7vf\nbGY3eTDAWG9XEQyedgZQEr5mVbjsLOBUYB/wNPAmM9tEMHzCQnd3i3+TmTcRDLAXay7B+DXzgN+b\n2cnARwbxuWLlA8+6++fM7OvAJ4Avx2kXK95nqSIYH+gtBFcq9x6LZxHBwGzN/fwbnAWcErYtIwix\n75vZ1H62UxXBKKHPD1CzjBPaU5DRdhnBuElrCYYMnwrMD5c93+uL86/NrHuoivKYdn25APipB4Oo\nVQNPAufGvPceDwZXW0vwxX4UaAG+Z2ZXAfHG/5kB1PSad5+7d3kwVPI2YOEgP1esNqD72P8LYV0D\nifdZFgLb3f1VD4Yp+FGv16xw9+Zwuq9aL+TY9tsH/C5s3992OkAwrIOkCO0pyGgz4FPu/uhxM80u\nBhp7Pb8EON/dm8zsCSBnGOttjZnuJLjjWEd46OOtwPsJxkx6S6/XNQPFveb1HhvGSfBzxdHux8aa\n6eTY/8kOwj/azCyN4K5ifX6Wft6/W2wNfdUa93aXA2ynHIJtJClCewoStXqC2452exS40YLhwzGz\nBRaMHtlbMXA4DISFBLf97Nbe/fpengKuDo+ZlxL85dvnYQ0L7mlR7MHw2n9HcNipt03Ayb3mfcDM\n0sxsHsGAZ5sH8bkStQM4J5x+NxDv88Z6GZgb1gTBKLJ96avWVRzbfjOAN4fL+9tOCxjro37KoGhP\nQaK2DugMDwPdBXyT4HDHmrCDtIb4t6n8DbAsPO6/meAQUrfbgHVmtsbdPxQz/xfA+QQjUjrwj+6+\nPwyVeAqBX5pZDsFfz5+O02YV8A0zs5i/6HcRhE0RwQiZLWZ2R4KfK1G3h7W9SLAt+tvbIKzhBuDX\nZtZEEJCFfTTvq9ZfEOwBbAw/4zNh+/6205sI7qUsKUKjpIoMwMy+CfzK3R83s7uAh9z950kuK+nM\n7Czg0+7+4WTXIiNHh49EBvbvQF6yixiDSoB/SXYRMrK0pyAiIj20pyAiIj0UCiIi0kOhICIiPRQK\nIiLSQ6EgIiI9/j+y2uZuZKmSvQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1e6e802cc88>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 绘制图\n",
    "costs = np.squeeze(d['costs'])\n",
    "plt.plot(costs)\n",
    "plt.ylabel('cost')\n",
    "plt.xlabel('iterations (per hundreds)')\n",
    "plt.title(\"Learning rate =\" + str(d[\"learning_rate\"]))\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
